<?php
/**
 * Класс предназначен для обработки данных связанных с правами доступа.
 * Этот класс по сути инструментарий для абстракции более высокого класса - Ассистента.
 *
 * Содержит функции для работы с
 * а) отношениями субъектов (таблица subject_relations)
 * б) правилами проверки прав (таблица acl_tree)
 * в) самими правами (acl_list)
 * г) реестром
 *
 * НЕ ИСПОЛЬЗОВАТЬ НАПРЯМУЮ
 *
 * @package default
 * @author Alexander Sibiryakov
 **/
class Model_AclAssistantLowLevel extends RL_Model2
{

//	public $acl_data;

	protected function init()
	{
		//$this->db = &$db;

		define("sr_table", "t_subject_relations");
		define("act_table", "t_acl_tree");
		define("al_node", "id");
		define("al_parent", "parent_id");
		define("ns_lft", "lft");
		define("ns_rgt", "rgt");

	}

	// выдаст список действий доступных субъектам для объекта $object_id
	public function getRights( $object_id, $subject_ids=array())
	{
		if (!isset($object_id) || $object_id === false || !isset($subject_ids)) return false;

		if (count($subject_ids)==0) return false;

		$_subject_ids = array();
		foreach($subject_ids as $sid) if (strlen($sid) > 0) $_subject_ids[] = 't_acl_list.id_subject = '.$sid;

		if (count($_subject_ids)==0) return false;

		$where_subjects = implode(' OR ', $_subject_ids);

		$sql = "SELECT t_acl_list.id_subject, t_acl_list.`action`, t_acl_list.opt, parent.id_object
FROM t_acl_tree AS node, t_acl_tree AS parent
LEFT OUTER JOIN t_acl_list ON parent.id_object = t_acl_list.id_acl_tree
WHERE (`node`.`".ns_lft."` BETWEEN `parent`.`".ns_lft."` AND `parent`.`".ns_rgt."`)
AND node.id_object = ".$object_id." AND (".$where_subjects.")
ORDER BY parent.`".ns_lft."` DESC";

//		print $sql;exit;

		// в запросе группировать по экшену нельзя
		// нужно выбрать "самое верхнее" действие (ближайшее с данным именем в сторону корня)
		$data     = $this->db->Query( $sql );
		return $data; 		// получили путь по дереву acl с элементами в каждом узле отфильтрованными по объекту.
	}

	// выдаст список отношений субъекта с другими субъектами
	public function getRelations( $id_object, $pid_object=NULL )
	{

		$pid_sql = $pid_object?"AND node.".al_parent." = ".$pid_object:"";
		$sql = "SELECT parent.`comment`, parent.id_object
			FROM t_subject_relations AS node,
			t_subject_relations AS parent
			WHERE (`node`.`".ns_lft."` BETWEEN `parent`.`".ns_lft."` AND `parent`.`".ns_rgt."`)
			AND node.id_object = ".$id_object." ".$pid_sql."
			#GROUP BY parent.id_object
			ORDER BY parent.`".ns_lft."` DESC";
		return $this->db->Query( $sql );
	}

	public function getLevelChildsById( $node_id, $tname="t_subject_relations" )
	// прямые дети нода таблица отношений субъектов
	{

		$sql = "SELECT l1.* FROM ".$tname."
			LEFT OUTER JOIN ".$tname." l1 ON l1.".al_parent." = ".$tname.".".al_node."
			WHERE ".$tname.".id = ".$node_id." AND l1.id_object<>0";

		return $this->db->Query($sql);
	}

	public function getAllChildsById( $node_id, $tname="t_acl_tree" )
	// все дети нода
	{
		$sql = "SELECT chld.* FROM ".$tname." parent
				LEFT OUTER JOIN ".$tname." chld ON chld.lft BETWEEN parent.lft AND parent.rgt
				WHERE parent.id = ".$node_id;

		return $this->db->Query($sql);
	}

	public function getLevelChildsByOid( $node_oid, $tname="t_subject_relations" )
	// прямые дети нода таблица отношений субъектов
	{

		$sql = "SELECT l1.* FROM ".$tname."
			LEFT OUTER JOIN ".$tname." l1 ON l1.".al_parent." = ".$tname.".".al_node."
			WHERE ".$tname.".id_object = ".$node_oid." AND l1.id_object<>0";

		return $this->db->Query($sql);
	}

	public function getPathWCond(
		$path /* =  array(   ['level'] => array("field"=>"value")  ) */,
		$tname = "t_subject_relations",
		$al_node = "id",
		$al_parent = "parent_id",
		$return_level = 1 // уровень по которому вернуть содержимое нода
	)
	{

		/*
		$path = array(  //test data
			"1"=>array("id_object"=>3),
			"4"=>array("id_object"=>8)
			);
		 */

		$maxlevel = 0;
		foreach( $path as $level=>$cond ) {
			foreach ($cond as $param=>$val) {
				$wheres[] = "l".$level.".".$param."='".$val."'";
			}

			if ( $maxlevel < $level ) { $maxlevel = $level; }
		}

		for ($i=1; $i<=$maxlevel; $i++) {
			$joins[] = "LEFT OUTER JOIN ".$tname." l".($i+1)." ON l".($i+1).".".$al_parent." = l".$i.".".$al_node;
		}

		$ret_sql = "l".$return_level.".*";

		$sql = "SELECT ".$ret_sql." FROM ".$tname." as l1 ".implode(" ", $joins)." WHERE ".implode(" AND ", $wheres);

		return $this->db->QueryOne( $sql );
	}

	public function setAclList ($acl_list, $update=False)
	{
		return $this->db->runInTransaction(
			array($this,'_setAcl'),
			array($this->db, $acl_list, $update, 't_acl_list'),
			true
		);
	} // end of function setAclList


	public function _setAcl ($db, $acl_list, $update=False, $tname='t_acl_list')
	{
		$save_list = array();
		$drop_list = array();

		/*
		// 61: сори, не понял
		foreach ($acl_list as $acl) {
			if (isset($acl['opt'])) {
				$save_list[] = $acl;
			} else {
				$drop_list[] = $acl;
			}
		}
		*/
		foreach ($acl_list as $acl) {
			if (isset($acl['opt'])) {
				$save_list[] = $acl;
				$drop_list[] = $acl;
			}
		}

		if (!$update) {
			// чистим все отношения subj и obj
			$objects_ids = array();
			$subject_ids = array();
			foreach( $acl_list as $alist) {
				$subject_ids[$alist['subject_id']] = $db->quote($alist['subject_id']);
				$objects_ids[$alist['object_id']] = $db->quote($alist['object_id']);
			}
			$e_subject_ids = implode(',', $subject_ids);
			$e_object_ids  = implode(',', $objects_ids);
			$sql = "DELETE FROM ".$tname."
				      WHERE id_acl_tree IN ($e_object_ids)
				        AND id_subject  IN ($e_subject_ids)"
				;

			$db->execute( $sql );
		} else {
			$where = array();
			// чистим отношения obj->act->sub, которые просят
			foreach( $drop_list as $alist) {
				$e_object_id  = $db->quote($alist['object_id']);
				$e_subject_id = $db->quote($alist['subject_id']);
				$e_action     = $db->quote($alist['act']);
				$where[] = "(id_acl_tree = $e_object_id
					        AND id_subject = $e_subject_id
					        AND action     = $e_action)";
			}
			$e_where = implode(' OR ', $where);
			// чистим то, что нужно удалить
			$sql = "DELETE FROM ".$tname." WHERE ".$e_where;
			$db->execute( $sql );
		}
		$this->_addAclList($save_list, $tname);
		return true;
	} // end of function setAcl

	// достраивает таблицу acl_tree добавляя тем самым правило для проверки прав
	public function _addAclTreeNode( $left_node_ido=NULL, $parent_ido=NULL, $id_object, $_comment )
	{
		$db = &$this->db;
		// $this->rh->ctx->db->db_prefix;
		$tname = "t_acl_tree";

		if ( ($left_node_ido==NULL)&&($parent_ido==NULL) ) { // наверное имеется ввиду узел
			$sql = "INSERT INTO ".$tname." VALUES(NULL, ".$id_object.", 0, 1, 2, '".$_comment."', DEFAULT )";
			return $db->Insert( $sql );
		}

		/*
		$sql = "LOCK TABLE ".$tname." WRITE";
		$db->Query( $sql );
		 */

		if ( is_numeric($left_node_ido)&&($left_node_ido!=NULL) ) {

			$sql = "SELECT ".ns_rgt." as myvar, ".al_parent." FROM ".$tname." WHERE ".ns_lft." = ".$left_node_ido;

		} elseif ( is_numeric($parent_ido) ) {

			$sql = "SELECT ".ns_lft." as myvar FROM ".$tname." WHERE ".al_node." = ".$parent_ido;

		}

		$result = $db->QueryOne( $sql );
		$myvar = $result['myvar'];

		if (isset($result['parent_id'])) $parent_ido = $result['parent_id'];


		$sql = "UPDATE ".$tname." SET ".ns_rgt." = ".ns_rgt." + 2 WHERE ".ns_rgt." > ".$myvar;
		$db->Query( $sql );

		$sql = "UPDATE ".$tname." SET ".ns_lft." = ".ns_lft." + 2 WHERE ".ns_lft." > ".$myvar;
		$db->Query( $sql );

		$sql = "INSERT INTO ".$tname."(id_object, ".al_parent.", ".ns_lft.", ".ns_rgt.", _comment) VALUES(".$id_object.",".$parent_ido.", ".($myvar+1).", ".($myvar+2).", ".$db->Quote($_comment).")";

		$insert = $db->Insert( $sql );

		return $insert;

	}

	/**
	 * Эта штука должна иметь массив со следующей структурой:
	 *
	 *	$acl_act['subject_id']
	 *	$acl_act['act']
	 *	$acl_act['object_id']
	 *	$acl_act['opt'] {2|1|0}
	 *	$acl_list - массив массивов $acl_act
	 *
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function _addAclList( $acl_list, $tname = "t_acl_list" ) {
		$db = &$this->db;

		foreach( $acl_list as $alist) {
			$sql = "INSERT INTO ".$tname." VALUES(NULL, ".$alist['subject_id'].", 'New item', '',
				'".$alist['act']."', ".$alist['opt'].", ".$alist['object_id']." )";

			$db->Insert( $sql );
		}

		return true;
	}

	/**
	 * Копирует список действий одного объекта другому
	 *
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function _copyAclList( array $subj_src_oid, $obj_src_oid, $obj_dest_oid )
	{

		if (count($subj_src_oid)==0) return false;

		foreach($subj_src_oid as $sid) $_subj_src_oid[] = 't_acl_list.id_subject = '.$sid;

		$where_subjects = "(".implode(' OR ', $_subj_src_oid).")";

		$sql = "INSERT INTO t_acl_list (id_subject, `comment`, `action`, opt, id_acl_tree)
			SELECT
			t_acl_list.id_subject,
			t_acl_list.`comment`,
			t_acl_list.`action`,
			t_acl_list.opt,
			".$obj_dest_oid." as id_acl_tree # destination object
			FROM t_acl_list
			LEFT OUTER JOIN t_acl_tree ON t_acl_list.id_acl_tree = t_acl_tree.id
			WHERE
			".$where_subjects." # roles that needs to be copied
			AND t_acl_tree.id_object = ".$obj_src_oid." #source object
			";

		return $this->db->Insert( $sql );
	}

	// достраивает таблицу subject_relations, добавляя нод, поидее для внутреннего использования
	public function _addSubjectRelation( $left_node_ido = NULL, $parent_ido = NULL, $id_object, $_comment )
	{
		$db = &$this->db;
		// $this->rh->ctx->db->db_prefix;
		$tname = "t_subject_relations";

		if ( ($left_node_ido==NULL)&&($parent_ido==NULL) ) { // видимо от нас хотят создать нод

			$sql = "INSERT INTO ".$tname."(".al_parent.", ".ns_lft.", ".ns_rgt.", id_object, comment, var) VALUES( 0, 1, 2,".$id_object.", ".$db->Quote($_comment).",'')";
			return $db->Insert($sql);
		}

		/*
		$sql = "LOCK TABLE ".$tname." WRITE";
		$db->Query( $sql );
		 */

		if ( is_numeric($left_node_ido)&&($left_node_ido!=NULL) ) {

			$sql = "SELECT ".ns_rgt." as myvar, parent_id FROM ".$tname." WHERE ".ns_lft." = ".$left_node_ido;

		} elseif ( is_numeric($parent_ido) ) {

			$sql = "SELECT ".ns_lft." as myvar FROM ".$tname." WHERE id = ".$parent_ido;

		}

		$result = $db->QueryOne( $sql );
		$myvar = $result['myvar'];

		if (isset($result['parent_id'])) $parent_ido = $result['parent_id'];


		$sql = "UPDATE ".$tname." SET ".ns_rgt." = ".ns_rgt." + 2 WHERE ".ns_rgt." > ".$myvar;
		$db->Query( $sql );

		$sql = "UPDATE ".$tname." SET ".ns_lft." = ".ns_lft." + 2 WHERE ".ns_lft." > ".$myvar;
		$db->Query( $sql );

		$sql = "INSERT INTO ".$tname."(".al_parent.", ".ns_lft.", ".ns_rgt.", id_object, comment) VALUES(".$parent_ido.", ".($myvar+1).", ".($myvar+2).",".$id_object.", ".$db->Quote($_comment).")";

		$insert = $db->Insert( $sql );

		/*
		$sql = "UNLOCK TABLES";
		$db->Query( $sql );
		 */

		return $insert;
	}

	//!a manipulator
	public function moveSubjectRelation ($node_id, $to_parent_id)
		/**
		 * Переместить отношение из одного узла в другой.
		 *
		 * @param mixed $node_id      структура данных перемещаемого узла, или его id.
		 * @param mixed $to_parent_id структура данных узла назначения, дитём котого
		 *                            сделать, или его id
		 * @access public
		 * @return boolean
		 */
	{
		return $this->db->runInTransaction(
			array($this,'_moveSubjectRelation'),
			array($this->db, $node_id, $to_parent_id),
			true
		);
	} // end of function moveSubjectRelation

	//!a manipulator
	public function removeSubjectRelation ($node_id)
		/**
		 * Удалить отношение.
		 *
		 * @param mixed $node_id      структура данных перемещаемого узла, или его id.
		 * @access public
		 * @return boolean
		 */
	{
		//return $this->_removeSubjectRelation($db, $node_id);
		return $this->db->runInTransaction(
			array($this,'_removeSubjectRelation'),
			array($this->db, $node_id),
			true
		);
	} // end of function moveSubjectRelation

	//!a function
	static public function _moveSubjectRelation($db, $node_id, $to_parent_id)
		/**
		 * Переместить узел (или поддерево с корнем) в другой узел, вставить в начало
		 * списка "детей".
		 *
		 * FIXME : 2007-10-26 : lucky
		 * можно вынести в отдельную библиотеку
		 *
		 * WARNING: ПЕРЕД вызовом.
		 * Позаботьтесь о блокировке таблицы или транзакции.
		 *
		 * @param DBAL  $db           база данных
		 * @param mixed $node_id      структура данных перемещаемого узла, или его id.
		 * @param mixed $to_parent_id структура данных узла назначения, дитём котого
		 *                            сделать, или его id
		 * @access public
		 * @return boolean
		 */
	{

		$status = false;

		// $this->rh->ctx->db->db_prefix;
		$tname = "t_subject_relations";

		// определим структуру данных с инфой о перемещаемом узле
		if (is_array($node_id)) { // нам передали готовую структуру
			$node = $node_id;
		} else { // найдем узел
			$sql = "SELECT ".al_node.",".al_parent.",".ns_lft.",".ns_rgt." FROM ".$tname
				." WHERE ".al_node." = ".$db->quote($node_id);
			$node = $db->QueryOne( $sql );
		}

		// определим структуру данных с инфой об узле назначения
		if (is_array($to_parent_id)) { // нам передали готовую структуру
			$to_parent = $to_parent_id;
		} else { // найдем родителя, в которого перемещаем
			$sql = "SELECT ".al_node.",".al_parent.",".ns_lft.",".ns_rgt." FROM ".$tname
				." WHERE ".al_node." = ".$db->quote($to_parent_id);
			$to_parent = $db->QueryOne( $sql );
		}

		if (!$node || !$to_parent) { // нет узлов
			// FIXME : 2007-10-26 : lucky
			// тут ст0ит грязно выругаться
			return false;
		}

		if ($node[al_parent] === $to_parent[al_node]) { // так это и есть родитель!
			// хе-хе... можно не суетиться...
			return true;
		}

		// поехали

		// выдернем часто испльзуемые переменные из структур
		$node_ns_lft      = $node[ns_lft];
		$node_ns_rgt      = $node[ns_rgt];
		$to_parent_ns_lft = $to_parent[ns_lft];
		$to_parent_ns_rgt = $to_parent[ns_rgt];

		// найдем ближайшего общего предка узлов
		// дерево меняется только в его пределах
		// это позволит нам ограничить объём изменяемых данных в таблице
		// (надеюсь оно того стоит, чтобы делать дополнительный запрос)
		$sql = "SELECT ".ns_lft.",".ns_rgt." FROM ".$tname
			.    " WHERE ".ns_lft."<".min($node_ns_lft, $to_parent_ns_lft)
			.      " AND ".ns_rgt.">".max($node_ns_rgt, $to_parent_ns_rgt)
			.    " ORDER BY ".ns_lft." DESC"
			.    " LIMIT 1"
			;
		$common = $db->QueryOne( $sql );
		if (!$common) { // хм.. нет общего предка? в таблице косяк
			// FIXME : 2007-10-26 : lucky
			// тут ст0ит грязно выругаться
			return false;
		}
		// общее условие для некоторых жадных запросов
		// чтобы "захватывали" данные лишь в пределах общего предка
		$and_range = " AND ".ns_lft.">".$common[ns_lft]
			.          " AND ".ns_rgt."<".$common[ns_rgt]
			;


		// далее все запросы можем сделать пачкой
		$sql = array();

		// сколько узлов перемещаем?
		$node_count = $node_ns_rgt - $node_ns_lft + 1;

		// пометим перемещаемое поддерево
		// дабы была возможность в дальнейшем исключать его из выборки
		//
		// установим lft в отрицательные значения
		$sql[] = "UPDATE ".$tname." SET ".ns_lft." = -".ns_lft
			.      " WHERE ".ns_lft.">=".$node_ns_lft
			.        " AND ".ns_rgt."<= ".$node_ns_rgt
			;

		// "удалим" перемещаемое поддерево из nested set
		$sql[] = "UPDATE ".$tname." SET ".ns_lft." = ".ns_lft." - ".$node_count
			.      " WHERE ".ns_lft.">=".$node_ns_rgt
			//.        " AND ".ns_lft.">=0" // HACK : 2007-10-26 : lucky
			//                                 оптимизация
			//                                 т.к. $to_parent_ns_lft > 0 всегда
			.          $and_range
			;
		$sql[] = "UPDATE ".$tname." SET ".ns_rgt." = ".ns_rgt." - ".$node_count
			.      " WHERE ".ns_rgt.">=".$node_ns_rgt
			.        " AND ".ns_lft.">=0"
			.          $and_range
			;

		// кто левее?
		if ($node_ns_lft < $to_parent_ns_lft) {
			// перемещаемый нод был левее, поэтому обновили узел назначения
			$to_parent_ns_lft -= $node_count;
			$to_parent_ns_rgt -= $node_count;
		}

		// расчистим место в узле назначения для перемещаемого поддерева
		// слева
		$sql[] = "UPDATE ".$tname." SET "
			.          ns_lft." = ".ns_lft." + ".($node_count ).","
			.          ns_rgt." = ".ns_rgt." + ".($node_count )
			.      " WHERE ".ns_lft." > ".$to_parent_ns_lft
			//.        " AND ".ns_lft.">=0" // HACK : 2007-10-26 : lucky
			//                                 оптимизация
			//                                 т.к. $to_parent_ns_lft > 0 всегда
			.          $and_range
			;
		// справа-сверху и внутри себя
		$sql[] = "UPDATE ".$tname." SET "
			.          ns_rgt." = ".ns_rgt." + ".($node_count )
			.      " WHERE ".ns_rgt." >=".$to_parent_ns_rgt
			.        " AND ".ns_lft." <=".$to_parent_ns_lft
			.        " AND ".ns_lft.">=0"
			.          $and_range
			;

		// расстояние, на которое нужно переместить поддерево
		//
		// FIXME : 2007-10-26 : lucky
		// $to_parent_ns_lft содержит поправку на $node_count (кто левее),
		// а $node_ns_lft -- нет
		// т.е. по смыслу эта формула не вполне корректна
		$move_length = ($to_parent_ns_lft - $node_ns_lft + 1);

		// теперь переместим все поддерево, попутно сняв выделение
		// поэтому -ns_lft
		$sql[] = "UPDATE ".$tname." SET "
			.         ns_lft." = -".ns_lft."+ ".$move_length.", "
			.         ns_rgt." = ".ns_rgt."+ ".$move_length
			.       " WHERE ".ns_lft."<0"
			;

		// установим нового родителя для корня перемещенного поддерева
		$sql[] = "UPDATE ".$tname." SET "
			.         al_parent." = ".$to_parent['id']
			.       " WHERE ".al_node."=".$node['id']
			;

		// ну все, побежали выполнять запросы..
		$status = true; // настроимся на хорошее ;)
		//$db->begin();
		foreach ($sql as $q) {
			//var_dumP($q);
			if (!$db->execute( $q )) { $status = false; break; }
		}
		//$db->end();

		return $status;
	}

	//!a function
	static public function _removeSubjectRelation($db, $node_id)
		/**
		 * Удалить поддерево.
		 *
		 * FIXME : 2007-10-26 : lucky
		 * можно вынести в отдельную библиотеку
		 *
		 * WARNING: ПЕРЕД вызовом.
		 * Позаботьтесь о блокировке таблицы или транзакции.
		 *
		 * @param DBAL  $db           база данных
		 * @param mixed $node_id      структура данных перемещаемого узла, или его id.
		 * @access public
		 * @return boolean
		 */
	{

		$status = false;

		//$this->rh->ctx->db->db_prefix;
		$tname = "t_subject_relations";

		// определим структуру данных с инфой о перемещаемом узле
		if (is_array($node_id)) { // нам передали готовую структуру
			$node = $node_id;
		} else { // найдем узел
			$sql = "SELECT ".ns_lft.",".ns_rgt." FROM ".$tname
				." WHERE ".al_node." = ".$db->quote($node_id);
			$node = $db->QueryOne( $sql );
		}

		if (!$node) { // нет узлов
			// FIXME : 2007-10-26 : lucky
			// тут ст0ит грязно выругаться
			return false;
		}

		// поехали

		// выдернем часто испльзуемые переменные из структур
		$node_ns_lft      = $node[ns_lft];
		$node_ns_rgt      = $node[ns_rgt];

		// далее все запросы можем сделать пачкой
		$sql = array();

		// сколько узлов удаляем?
		$node_count = $node_ns_rgt - $node_ns_lft + 1;

		// пометим перемещаемое поддерево
		// дабы была возможность в дальнейшем исключать его из выборки
		//
		// установим lft в отрицательные значения
		$sql[] = "DELETE FROM ".$tname
			.      " WHERE ".ns_lft.">=".$node_ns_lft
			.        " AND ".ns_rgt."<= ".$node_ns_rgt
			;

		// "удалим" поддерево из nested set
		$sql[] = "UPDATE ".$tname." SET ".ns_lft." = ".ns_lft." - ".$node_count
			.      " WHERE ".ns_lft.">=".$node_ns_rgt
			;
		$sql[] = "UPDATE ".$tname." SET ".ns_rgt." = ".ns_rgt." - ".$node_count
			.      " WHERE ".ns_rgt.">=".$node_ns_rgt
			;


		// ну все, побежали выполнять запросы..
		$status = true; // настроимся на хорошее ;)
		//$db->begin();
		foreach ($sql as $q) {
			//var_dumP($q);
			if (!$db->execute( $q )) { $status = false; break; }
		}
		//$db->end();

		return $status;
	}

	public function getSubjectByOid ( $oid )
	{
		$tname = "t_subject_relations";
		$sql = "SELECT * FROM ".$tname." WHERE id_object = ".$oid;
		return $this->db->QueryOne( $sql );
	}

	public function getAclTreeNode ( $oid )
	{
		$tname = "t_acl_tree";
		$sql = "SELECT * FROM ".$tname." WHERE id_object = ".$oid;
		return $this->db->QueryOne( $sql );
	}

	/**
	 * Возвращает разность глубин двух узлов. Узлы извлекаются по Oid.
	 *
	 * @param $node1_oid, $node2_oid int идентификаторы узлов
	 * @return int
	 * @author Alexander Sibiryakov
	 **/
	public function getDepthBtwNodes( $node1_oid, $node2_oid ) {
		$tname = "t_acl_tree";
		$sql = "SELECT node.id_object, (COUNT(parent.id)-1) AS depth
		FROM ".$tname." AS node,
		".$tname." AS parent
		WHERE node.lft BETWEEN parent.lft AND parent.rgt AND
		(node.id_object = ".$node1_oid."
		OR node.id_object = ".$node2_oid.")
		GROUP BY node.lft
		ORDER BY node.lft";
		foreach ($this->db->Query($sql) as $i=>$depth) {
			$depths[ $depth['id_object'] ] = $depth['depth'];
		}
		if (count($depths)!=2) return false; else
			return abs($depths[$node2_oid] - $depths[$node1_oid]);

	}
}

?>
